# logger = logging.getLogger(__name__)  # 关键导入语句
import json
import random
import re
import time

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, authenticate, get_user_model, update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import Group, Permission
# from .permissions import HasPerm
from django.contrib.auth.views import LoginView
from django.contrib.contenttypes.models import ContentType
# from django.core import serializers
from django.core.cache import cache
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.exceptions import PermissionDenied
# 避免直接读取 request.body（存在解析风险）
from rest_framework.parsers import JSONParser
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.views import TokenObtainPairView

# from jewelry.serializers import UserSerializer, UserCreateSerializer
from account.serializers import UserSerializer, CustomTokenSerializer, PasswordResetSerializer
from django.db.models import Prefetch
from utils.get_permission import get_permission_names
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from django.db import IntegrityError
from rest_framework import permissions

User = get_user_model()


class CustomLoginView(LoginView):
    template_name = 'account/login.html'
    success_url = '/'  # 重定向到首页（优先级高于 LOGIN_REDIRECT_URL）

    def form_valid(self, form):
        """处理表单验证成功后的逻辑"""
        # 调用父类方法创建会话（关键）
        response = super().form_valid(form)
        # 可选：自定义 Cookie（非必需，用于前端同步状态）
        response.set_cookie('is_authenticated', 'true', max_age=3600)
        # 直接返回重定向响应
        return response

    def form_invalid(self, form):
        """处理表单验证失败逻辑（账号密码错误）"""
        # 记录登录失败日志（可选）
        self.log_failed_attempt(form)

        # 添加错误提示信息
        messages.error(
            self.request,
            _("请输入正确的用户名和密码。注意：大小写敏感。"),
            extra_tags="login_error"  # 用于前端定位错误消息
        )

        # 返回带有错误状态的响应
        return self.render_to_response(self.get_context_data(form=form))

    def log_failed_attempt(self, form):
        """记录登录失败尝试（安全审计）"""
        username = form.cleaned_data.get('username', 'unknown')
        client_ip = self.get_client_ip()

    def get_client_ip(self):
        """获取客户端真实IP（需考虑代理情况）"""
        x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = self.request.META.get('REMOTE_ADDR')
        return ip


# @method_decorator(csrf_exempt, name='dispatch')
class LoginView(TokenObtainPairView):
    """用密码登录的接口"""
    permission_classes = [AllowAny]  # 所有用户都可访问此接口
    serializer_class = CustomTokenSerializer

    def post(self, request, *args, **kwargs):
        # 调用父类方法获取 JWT:这一步非常关键,因为JWT验证不会创建 Django 会话，因此模板中 user.is_authenticated 始终为 False。
        # 通过这一步创建user.is_authenticated(创建 Django 会话),这样在模版中user.is_authenticated就会是true
        response = super().post(request, *args, **kwargs)
        # 手动认证用户并创建会话
        username = request.data.get('username')
        password = request.data.get('password')
        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)  # 关键：创建 Django 会话
            response.set_cookie('sessionid', request.session.session_key)  # 同步 Cookie
        return response


class SendSmsCode(APIView):
    """发送短信验证码的接口"""
    authentication_classes = []  # 明确禁用所有认证类
    permission_classes = [AllowAny]  # 所有用户都可访问此接口
    parser_classes = [JSONParser]

    def post(self, request, *args, **kwargs):
        """发送短信验证码"""
        phone = request.data.get('phone')  # 直接通过 request.data 获取

        # 手机号格式校验
        if not re.match(r'^1[3-9]\d{9}$', phone):
            return Response({'detail': '手机号格式错误', "status_origin": 210}, status=210)  # 应该是400

        # 获取验证用户
        user = User.objects.filter(phone=phone).first()
        if not user:
            return Response({'detail': '手机号未注册,请联系管理员', "status_origin": 210}, status=210)  # 应该是403

        # 检查是否员工
        if user and not user.is_staff:
            return Response({'detail': '不是合法用户', "status_origin": 210}, status=210)  # 应该是403

        # 激活状态检查
        if user and not user.is_active:
            return Response({'detail': '账户未激活，请联系管理员', "status_origin": 210}, status=210)  # 应该是403

        # 生成6位验证码:使用更安全的验证码生成方式secrets
        import secrets
        code = ''.join(secrets.choice('0123456789') for _ in range(6))  # 替换 random.choices

        # 获取模版id
        template_id = settings.TENCENT_SMS_TEMPLATE.get('login')

        # 存储到缓存（5分钟有效）
        cache_key = f'sms_code:{phone}'
        cache.set(cache_key, {'code': code, 'send_time': time.time()}, 300)

        # 调用腾讯短信接口
        try:
            print(f'验证码：{code}')  # 实际使用时删除这行,打开sms行
            # 发送短信,python3.12以后,需要把源码httpclient.py改一下62行处,把encoding="utf=8"去掉,3.12版本已经自己加上了,不要重复出错
        # send_sms_single(phone, template_id, [code, ])
        except Exception as e:
            return Response({'detail': '短信发送失败，请稍后重试'}, status=400)

        # 记录发送状态到session
        request.session['sms_phone'] = phone
        return Response({'detail': '短信发送成功'}, status=200)


class VerifySmsCode(TokenObtainPairView):
    """验证短信码并登录"""
    permission_classes = [AllowAny]  # 所有用户都可访问此接口

    def post(self, request, *args, **kwargs):
        phone = request.session.get('sms_phone')
        user_code = json.loads(request.body).get('code')

        if not (phone and user_code):
            # return Response({'detail': '验证流程异常'}, status=400)
            return Response({'detail': '验证流程异常', "status_origin": 210}, status=210)

        # 从缓存获取验证码
        cache_key = f'sms_code:{phone}'
        stored_data = cache.get(cache_key)

        # 验证码校验
        if not stored_data or stored_data['code'] != user_code:
            # return Response({'detail': '验证码错误或已过期'}, status=400)
            return Response({'detail': '验证码错误或已过期', "status_origin": 210}, status=210)

        # 验证码有效期检查（5分钟）
        if time.time() - stored_data['send_time'] > 300:
            # return Response({'detail': '验证码已过期'}, status=400)
            return Response({'detail': '验证码已过期', "status_origin": 210}, status=210)

        # 清理已使用的验证码
        cache.delete(cache_key)

        # 1. 获取验证用户
        user = User.objects.filter(phone=phone).first()

        # 2. 检查用户是否内部员工
        if not user.is_staff:
            # return Response({'detail': '不是合法用户'}, status=400)
            return Response({'detail': '不是合法用户', "status_origin": 210}, status=210)

        # 3. 检查用户是否激活
        if not user.is_active:
            # return Response({'detail': '您还未激活'}, status=400)
            return Response({'detail': '您还未激活', "status_origin": 210}, status=210)
        if user is not None:
            login(request, user)  # 关键：创建 Django 会话

            # 更新最后登录时间
            user.last_login = timezone.now()  # ⏰更新最后登录时间为当前时间,数据库有8小时时差,但html自动会转回来
            user.save()
            refresh = RefreshToken.for_user(user)

            return Response({
                'userid': user.id,
                'username': user.username,
                'phone': phone,
                'refresh': str(refresh),
                'access': str(refresh.access_token),  # 即是jwt的Token
                'groups': list(user.groups.values_list('name', flat=True)),
                'roles': list(user.groups.values_list('name', flat=True)),
                'permissions': list(user.get_all_permissions()),
            }, status=200)
        else:
            # return Response({'detail': '您还未注册,请联系管理员注册'}, status=400)
            return Response({'detail': '您还未注册,请联系管理员注册', "status_origin": 210}, status=210)


# ################### 0.权限控制类 #################################
class IsOwnerOrAdmin(permissions.BasePermission):
    """
    允许：1) 超级用户 2) 拥有超级权限组的成员 3) 操作自身数据的用户
    """
    SUPER_GROUP_NAME = "超级权限"  # 定义超级权限组标识:拥有组里"超级权限"的

    def has_object_permission(self, request, view, obj):
        # 条件1：检查是否超级用户
        if request.user.is_superuser:
            return True

        # 条件2：检查用户是否属于超级权限组
        if self._has_super_group(request.user):
            return True

        # 条件3：检查是否操作自身数据
        return obj == request.user

    def _has_super_group(self, user):
        """检查用户是否属于超级权限组"""
        return user.groups.filter(name=self.SUPER_GROUP_NAME).exists()


# ################### 1.用户管理 #################################
# 1.用户管理: 查
class UserManageView(APIView):
    permission_classes = [AllowAny]
    authentication_classes = [SessionAuthentication]  # 启用会话认证

    def get(self, request):
        if request.GET.get('is_ajax'):
            ownUser = request.user  # 自己
            ownID = ownUser.id  # 自己的id
            ownUser_dict = {
                'id': str(ownUser.id),
                'username': ownUser.username,
                'email': ownUser.email,
                'phone': ownUser.phone,
                'is_staff': ownUser.is_staff,
                'is_active': ownUser.is_active,
                'last_login': ownUser.last_login,
                'groups': list(ownUser.groups.values_list('id', 'name')),
                'permissions': get_permission_names(ownUser),  # 用自定义函数转换['权限名1', '权限名2', '权限名3',]
                'direct_permissions': list(ownUser.user_permissions.all().values_list('id', 'codename', 'name')),
                'group_permissions': list(sorted({
                    perm.name
                    for group in ownUser.groups.all()
                    for perm in group.permissions.all()
                })),
            }

            # 如果操作员是超级用户 或者 所属组"超级权限"提取所有用户(内部+外部),否则则提取自己
            if ownUser.has_super_perm():
                keyword = request.GET.get('keyword', "").strip()  # 要搜索的用户名关键字,默认值""
                user_select = request.GET.get('userSelect', "internal")   # internal(默认值) 和 external

                # 1. 创建无预取的基础查询集
                base_queryset = User.objects.order_by('-date_joined').exclude(username='admin')
                # 1-1. 关键词搜索条件（多字段模糊匹配）
                if keyword:
                    base_queryset = base_queryset.filter(
                        Q(username__icontains=keyword) |
                        Q(email__icontains=keyword) |
                        Q(phone__icontains=keyword)
                    )

                # 1-2. 用户类型筛选（is_staff精确匹配）
                if user_select in ['internal', 'external']:
                    is_staff_value = (user_select == 'internal')
                    base_queryset = base_queryset.filter(is_staff=is_staff_value)

                # 2. 分页器仅计算数量不加载数据
                paginator = Paginator(base_queryset, per_page=10)
                # 3. 获取当前页对象（未加载数据）
                page_number = request.GET.get('page', 1)
                try:
                    page_obj = paginator.page(page_number)  # page_number从请求参数获取
                except EmptyPage:
                    # 创建空Page对象
                    page_obj = paginator.page(1) if paginator.num_pages > 0 else None

                # 4. 提取当前页的用户ID列表
                user_ids = page_obj.object_list.values_list('id', flat=True)  # 仅查ID列(每次只提一页per_page个,节省内存和开销)
                pagination = {
                    "current_page": page_obj.number,
                    "total_pages": paginator.num_pages,
                    "total_items": paginator.count,
                    "has_prev": page_obj.has_previous(),
                    "has_next": page_obj.has_next(),
                }
                # 5. 通过ID精确加载当前页用户+预取关联
                page_obj = User.objects.filter(id__in=user_ids).prefetch_related(
                    Prefetch('groups', queryset=Group.objects.prefetch_related('permissions')),
                    'user_permissions'
                ).order_by('-date_joined')
            else:
                pagination = {
                    "current_page": 1,
                    "total_pages": 1,
                    "total_items": 1,
                    "has_prev": False,
                    "has_next": False,
                }
                page_obj = User.objects.filter(id=ownID).prefetch_related(
                    Prefetch('groups', queryset=Group.objects.prefetch_related('permissions')),
                    'user_permissions'
                )
            # 所有的组
            all_groups = Group.objects.all().prefetch_related('permissions').order_by('-id')

            context = {
                'ownID': str(ownID),
                'ownUser_is_superuser': ownUser.is_superuser,   # 是否是超级用户(不包括组里的"超级权限")
                'ownUser_permissions': list(get_permission_names(ownUser)),  # 自己所有的权限['权限名1', '权限名2', '权限名3',]
                'ownUser': ownUser_dict,
                'is_ajax': True,
                'pagination': pagination,  # 分页
                'users': [{
                    'id': str(user.id),
                    'username': user.username,
                    'email': user.email,
                    'phone': user.phone,
                    'is_staff': user.is_staff,
                    'is_active': user.is_active,
                    'is_superuser': user.is_superuser,
                    'last_login': user.last_login,
                    'groups': list(user.groups.values_list('id', 'name')),
                    'permissions': get_permission_names(user),  # 用自定义函数转换['权限名1', '权限名2', '权限名3',]
                    'direct_permissions': list(user.user_permissions.all().values_list('id', 'codename', 'name')),
                    'group_permissions': list(sorted({
                        perm.name
                        for group in user.groups.all()
                        for perm in group.permissions.all()
                    })),
                } for user in page_obj],
                'groups': [{
                    'id': group.id,
                    'name': group.name,
                    'permissions': list(group.permissions.values_list('id', 'codename', 'name'))
                } for group in all_groups],
            }
            html = render_to_string('account/rbac/user_manage.html', context, request=request)

            if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                # 返回JSON给分页
                return JsonResponse({
                    "table_html": html,
                    "pagination": pagination,  # 分页
                    "context": context,
                })

            # 返回JSON给base主页
            return JsonResponse({
                'html': html,
                'data': {'tabId': 'user_manage'},
                'js_files': [{
                    'type': 'esm',  # 声明模块类型
                    'path': '/static/js/user_manage.js',
                    'inlineCode': None
                }],

            })
        else:
            # 完整页面访问时返回基础模板
            return render(request, 'base.html')


# 用户管理:增删改
# @method_decorator(csrf_exempt, name='dispatch')
class UserViewSet(ModelViewSet):
    # permission_classes = [AllowAny]  # 允许匿名访问
    permission_classes = [IsAuthenticated, IsOwnerOrAdmin]  # 必须登录用户并且具有IsOwnerOrAdmin权限才能访问
    queryset = User.objects.all().prefetch_related('groups').order_by('-id')
    serializer_class = UserSerializer

    def create(self, request, *args, **kwargs):
        """ post:创建新对象 """
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 触发验证
        self.perform_create(serializer)  # 内部流程：最终会调用 serializer.save() 触发序列化器的 create() 方法
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=201, headers=headers)

    # 删除用户（对应前端DELETE）
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=204)

    def get_serializer_class(self):
        """动态选择序列化器"""
        if self.action in ['update', 'partial_update', 'create']:
            return UserSerializer
        return super().get_serializer_class()

    def update(self, request, *args, **kwargs):
        """处理PUT请求的核心方法"""
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        # 权限检查：用户只能修改自己，超级管理员可修改所有人
        # if not (request.user.is_superuser or instance == request.user):
        #     return Response({"detail": "无操作权限"}, status=status.HTTP_403_FORBIDDEN)

        serializer = self.get_serializer(
            instance,
            data=request.data,
            partial=partial
        )
        serializer.is_valid(raise_exception=True)

        self.perform_update(serializer)

        # 若修改的是当前用户，刷新会话
        if request.user == instance:
            updated_user = get_user_model().objects.get(pk=instance.pk)
            update_session_auth_hash(request, updated_user)

        # 更新会话认证哈希（保留登录状态）
        # update_session_auth_hash(request, request.user)

        # 返回更新后的完整数据
        return Response(serializer.data)

    # def perform_update(self, serializer):
    #     # 可在此添加审计日志等逻辑
    #     serializer.save()


# 重置密码操作（对应重置按钮）
class PasswordResetView(APIView):
    permission_classes = [IsAuthenticated]  # 确保用户已登录

    def post(self, request):
        serializer = PasswordResetSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data
        auth_method = data['auth_method']
        user_id = data['user_id']

        # 获取目标用户逻辑
        try:
            target_user = User.objects.get(id=user_id)
        except User.DoesNotExist:
            raise PermissionDenied("操作的用户不存在")

        # 核心权限验证: 必须是自己或者管理员
        if not (request.user.is_superuser or request.user == target_user):
            raise PermissionDenied("无权操作此账户")

        try:
            if auth_method == 'sms':
                self._process_sms_auth(data, request, target_user)  # 传入完整request
            elif auth_method == 'newPassword':
                self._process_new_password_auth(data, request, target_user)
            else:
                return Response({"error": "无效验证方式"}, status=400)
        except (ValueError, PermissionDenied) as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            return Response({"error": "内部错误"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response({"success": "密码已重置"})

    def _process_sms_auth(self, data, request, target_user):
        """处理短信验证（支持未登录用户）"""
        phone = data['phone']
        sms_code = data['sms_code']
        operator_id = request.user.id
        new_password = data.get('new_password')
        confirm_password = data.get('confirm_password')

        # 基础验证
        if not all([new_password, confirm_password]):
            raise ValueError("缺少必要参数")
        if new_password != confirm_password:
            raise ValueError("新密码与确认密码不一致")
        if target_user.phone != phone and request.user.phone != phone:
            raise ValueError("手机号与目标用户不匹配")

        # django安全密码强度规范检查
        # try:
        #     validate_password(new_password)
        # except ValidationError as e:
        #     raise ValueError(f"密码不符合要求: {', '.join(e.messages)}")

        # 验证码校验
        key = f'sms_code_{phone}'
        cached_code = cache.get(key)
        if not cached_code:
            raise ValueError("验证码已过期或不存在")
        try:
            stored_code, stored_user_id = cached_code.split(':', 1)
            stored_user_id = int(stored_user_id)
        except (ValueError, TypeError) as e:
            cache.delete(key)
            raise ValueError("验证码格式错误") from e

        if stored_user_id != operator_id:
            cache.delete(key)
            raise ValueError("用户身份不匹配")
        if sms_code != stored_code:
            raise ValueError("验证码不正确")

        cache.delete(key)
        target_user.set_password(new_password)
        target_user.save()
        return request.user

    def _process_new_password_auth(self, data, request, target_user):
        old_password = data.get('old_password')
        new_password = data.get('new_password')
        confirm_password = data.get('confirm_password')

        if not all([old_password, new_password, confirm_password]):
            raise ValueError("缺少必要参数")
        if new_password != confirm_password:
            raise ValueError("新密码与确认密码不一致")

        if not target_user.check_password(old_password):
            raise ValueError("旧密码错误")

        # try:
        #     validate_password(new_password)
        # except ValidationError as e:
        #     raise ValueError(f"密码不符合要求: {', '.join(e.messages)}")

        target_user.set_password(new_password)
        target_user.save()

        # 修改密码后会话处理
        if request.user == target_user:
            # 用户修改自己密码时更新会话
            update_session_auth_hash(request, target_user)
        else:
            # 管理员修改他人密码时不需要更新会话
            pass

        return target_user


@method_decorator(login_required, name='dispatch')
class SendSMSView(APIView):
    """短信验证码发送接口（支持频率控制）"""
    THROTTLE_RATES = {'send_sms': '3/hour'}  # 每小时最多3次:

    def post(self, request):
        target_user_id = request.data.get('user_id')
        operator_id = request.user.id

        # 参数验证
        phone = request.data.get('phone')
        if not self._validate_phone(phone):
            # return Response({"error": "手机号格式错误"}, status=status.HTTP_400_BAD_REQUEST)
            return Response({"error": "手机号格式错误", "status_origin": 210}, status=210)

        # 权限限制:只有自己和管理员的手机号才能接收短信
        phone_exists = User.objects.filter(
            Q(id=target_user_id, phone=phone) | Q(id=operator_id, phone=phone, is_superuser=True)
        ).exists()
        if not phone_exists:
            # return Response({"error": "此手机号没有权限接收短信"}, status=status.HTTP_400_BAD_REQUEST)
            return Response({"error": "此手机号没有权限接收短信", "status_origin": 210}, status=210)

        # 频率控制
        if self._check_request_frequency(phone):
            # return Response({"error": "请求过于频繁，请稍后再试"}, status=status.HTTP_429_TOO_MANY_REQUESTS)
            return Response({"error": "请求过于频繁，请稍后再试", "status_origin": 210}, status=210)

        # 生成验证码
        code = self._generate_code()
        # 获取模版id
        template_id = settings.TENCENT_SMS_TEMPLATE.get('login')

        # 调用短信平台
        try:
            # send_sms_single(phone, template_id, [code, ])
            print('验证码:', code)
        except Exception as e:
            # return Response({"error": "短信服务暂时不可用"}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
            return Response({"error": "短信服务暂时不可用", "status_origin": 210}, status=210)

        # 缓存验证码
        cache.set(
            key=f'sms_code_{phone}',
            value=f'{code}:{operator_id}',  # 绑定用户ID防篡改:绑定的是操作员的id
            timeout=600  # 10分钟有效期
        )

        return Response({"success": True})

    def _validate_phone(self, phone):
        """手机号格式验证"""
        return phone and re.match(r'^1[3-9]\d{9}$', phone)

    def _check_request_frequency(self, phone):
        """频率校验逻辑(1小时只能3次)"""
        cache_key = f'sms_freq_{phone}'
        count = cache.get(cache_key, 0)
        if count >= 3:
            return True
        cache.set(cache_key, count + 1, timeout=3600)
        return False

    def _generate_code(self):
        """生成6位数字验证码"""
        return str(random.randint(100000, 999999))


# ########################## 2.组权分配 ##############################
# 2.组权分配
def groupPermManage(request):
    # user = request.user
    # 所有用户
    user_list = User.objects.all().order_by('-date_joined').prefetch_related('groups', 'user_permissions')

    # user_groups = [group.name for group in user.groups.all()]  # 自己所属的组(角色)
    # user_permissions = get_permission_names(user)  # 用自定义函数取:包含用户权限+组权限
    all_groups = Group.objects.all().prefetch_related('permissions').order_by('-id')  # 所有的组
    permissions = get_custom_permissions()  # 自定义函数提取UserInfo里定义的所有权限

    # 提取一级分类
    # main_perms = sorted([{"id": p.id, "name": p.name, "codename": p.codename} for p in permissions if len(p.codename.split('.')) == 1], key=lambda x: x['id'])

    context = {
        'users': [{
            'id': user.id,
            'username': user.username,
            'email': user.email,
            'phone': user.phone,
            'is_staff': user.is_staff,
            'is_active': user.is_active,
            'is_superuser': user.is_superuser,
            'last_login': user.last_login,
            'groups': list(user.groups.values_list('id', 'name')),
            'permissions': get_permission_names(user),  # 用自定义函数转换['权限名1', '权限名2', '权限名3',]
            'direct_permissions': list(user.user_permissions.all().values_list('id', 'codename', 'name')),
            # 'group_permissions': list(user.get_group_permissions())  # 默认去重
            'group_permissions': get_raw_group_permissions_optimized(user)  # 自定义不去重
        } for user in user_list],
        'groups': [{
            'id': group.id,
            'name': group.name,
            'permissions': list(group.permissions.values_list('id', 'codename', 'name'))
        } for group in all_groups],
        'permissions': [{
            'id': permission.id,
            'name': permission.name,
            'codename': permission.codename
        } for permission in permissions],

    }

    html = render_to_string('account/rbac/group_perm_manage.html', context, request=request)
    # 返回 JSON，包含 HTML + data
    return JsonResponse({
        'html': html,
        'data': {'tabId': 'group_perm_manage'},
        'js_files': [{
            'type': 'esm',  # 声明模块类型
            'path': '/static/js/group_perm_manage.js',
            'inlineCode': None
        }],
    })


# 组名管理
@csrf_exempt  # 根据CSRF配置决定是否豁免
@require_POST  # 确保只接受POST请求
def groupNameManage(request):
    try:
        # 解析前端JSON数据
        data = json.loads(request.body)
        group_id = data.get('id')
        group_name = data.get('name').strip()  # 清理空格

        # 验证数据有效性[[6]]
        if not group_name or len(group_name) > 50:
            return JsonResponse({
                'status': 'error',
                'code': 400,
                'message': '组名无效：长度1-50字符'
            }, status=400)
        try:
            # 核心逻辑：存在则更新，不存在则创建
            group, created = Group.objects.update_or_create(
                id=group_id,  # 查询条件
                defaults={'name': group_name}  # 更新字段
            )
        except IntegrityError:
            return JsonResponse({
                'status': 'error',
                'code': 409,
                'message': '组名已存在'
            }, status=409)

        # 标准化响应
        return JsonResponse({
            'status': 'success',
            'code': 200,
            'action': 'created' if created else 'updated',
            'group_id': group.id,
            'group_name': group.name
        })

    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'code': 400,
            'message': '无效的JSON数据'
        }, status=400)

    except Exception as e:
        # 全局异常处理
        return JsonResponse({
            'status': 'error',
            'code': 500,
            'message': f'服务器错误: {str(e)}'
        }, status=500)


# 删除组名
def groupDeleteManage(request, group_id):
    try:
        # 不再从请求体中获取group_id，而是从URL参数中获取
        # 验证group_id的有效性（例如是否为正整数）
        # 允许组ID=0（root组）
        if group_id is None:  # 严格检查None
            return JsonResponse({
                'status': 'error',
                'code': 400,
                'message': '组ID无效：未提供组ID'
            }, status=400)

        if group_id < 0:  # 负数永远无效
            return JsonResponse({
                'status': 'error',
                'code': 400,
                'message': '组ID必须为正整数'
            }, status=400)

        try:
            group = Group.objects.get(id=group_id)
            group_id = group.id
            group_name = group.name
            group.delete()  # 执行删除
            return JsonResponse({
                'status': 'success',
                'code': 200,
                'group_id': group_id,
                'message': f'组 [{group_name}] 已删除'
            })
        except Group.DoesNotExist:
            return JsonResponse({
                'status': 'error',
                'code': 404,
                'message': '组不存在'
            }, status=404)

    except Exception as e:
        return JsonResponse({'status': 'error', 'code': 500, 'message': f'服务器错误: {str(e)}'}, status=500)


# 组关联的权限:更新或者新增
def update_group_permissions(request, group_id):
    if request.method != 'PUT':
        return JsonResponse(
            {"status": "error", "code": 405, "message": "仅支持PUT方法"},
            status=405
        )

    try:
        data = json.loads(request.body)
        group_name = data.get('groupName', '').strip()
        permissions = data.get('permissions', [])
    except (json.JSONDecodeError, KeyError):
        return JsonResponse(
            {"status": "error", "code": 400, "message": "无效的请求数据"},
            status=400
        )

    # 1. 数据有效性验证
    if not 1 <= len(group_name) <= 50:
        return JsonResponse(
            {
                "status": "error",
                "code": 400,
                "message": "组名长度需在1-50字符之间",
                "field": "groupName"
            },
            status=400
        )

    # 2. 前置重复性检查
    if Group.objects.filter(name=group_name).exclude(id=group_id).exists():
        return JsonResponse(
            {
                "status": "error",
                "code": 409,
                "message": f"组名'{group_name}'已被使用",
                "suggestions": ["销售二组", f"{group_name}_备份"]  # 提供解决建议
            },
            status=409
        )

    try:
        # 3. 原子操作保证数据一致性
        with transaction.atomic():
            # 4. 获取或更新组
            group, created = Group.objects.update_or_create(
                id=group_id,
                defaults={'name': group_name}
            )

            # 5. 更新权限
            group.permissions.clear()
            perm_objs = Permission.objects.filter(id__in=permissions)
            group.permissions.add(*perm_objs)

            return JsonResponse({
                "status": "success",
                "group_id": group.id,
                "groupName": group.name,
                "permissions": [
                    [p.id, p.codename, p.name]
                    for p in perm_objs
                ]
            })

    except IntegrityError as e:
        # 6. 处理并发冲突
        return JsonResponse(
            {
                "status": "error",
                "code": 409,
                "message": "组名冲突，请稍后重试",
                "detail": str(e)
            },
            status=409
        )

    except Exception as e:
        # 7. 全局异常处理
        return JsonResponse(
            {
                "status": "error",
                "code": 500,
                "message": "服务器内部错误",
                # "trace_id": generate_trace_id()  # 用于日志追踪
            },
            status=500
        )


# ########################## 3.权限管理 ##############################
# 3.权限管理
# @permission_required('account.Can_modify_user_permissions')
def rbac_manager(request):
    ownUser = request.user
    ownID = ownUser.id

    # user_list = User.objects.all().order_by('-date_joined').prefetch_related('groups', 'user_permissions')  # 所有用户列表
    all_groups = Group.objects.all().prefetch_related('permissions').order_by('-id')  # 所有的组
    all_permissions = get_custom_permissions()  # 自定义函数提取UserInfo里定义的所有权限

    # 如果操作员是超级用户 或者 所属组"超级权限"提取所有用户(内部+外部),否则则提取自己
    if ownUser.has_super_perm():
        keyword = request.GET.get('keyword', "").strip()  # 要搜索的用户名关键字,默认值""
        # 1. 创建无预取的基础查询集(只创建内部用户)
        base_queryset = User.objects.order_by('-date_joined').filter(is_staff=True).exclude(username='admin')
        # 2. 关键词搜索条件（多字段模糊匹配）
        if keyword:
            base_queryset = base_queryset.filter(
                Q(username__icontains=keyword) |
                Q(email__icontains=keyword) |
                Q(phone__icontains=keyword)
            )
        # 3. 分页器仅计算数量不加载数据
        paginator = Paginator(base_queryset, per_page=10)
        # 4. 获取当前页对象（未加载数据）
        page_number = request.GET.get('page', 1)
        page_obj = paginator.page(page_number)  # page_number从请求参数获取
        # 5. 提取当前页的用户ID列表
        user_ids = page_obj.object_list.values_list('id', flat=True)  # 仅查ID列(每次只提一页per_page个,节省内存和开销)
        pagination = {
            "current_page": page_obj.number,
            "total_pages": paginator.num_pages,
            "total_items": paginator.count,
            "has_prev": page_obj.has_previous(),
            "has_next": page_obj.has_next(),
        }
        # 5. 通过ID精确加载当前页用户+预取关联
        page_obj = User.objects.filter(id__in=user_ids).prefetch_related(
            Prefetch('groups', queryset=Group.objects.prefetch_related('permissions')),
            'user_permissions'
        ).order_by('-date_joined')
    else:
        pagination = {
            "current_page": 1,
            "total_pages": 1,
            "total_items": 1,
            "has_prev": False,
            "has_next": False,
        }
        page_obj = User.objects.filter(id=ownID).prefetch_related(
            Prefetch('groups', queryset=Group.objects.prefetch_related('permissions')),
            'user_permissions'
        )
    context = {
        'pagination': pagination,  # 分页
        'users': [{
            'id': user.id,
            'username': user.username,
            'email': user.email,
            'phone': user.phone,
            'is_staff': user.is_staff,
            'is_active': user.is_active,
            'is_superuser': user.is_superuser,
            'last_login': user.last_login,
            'groups': list(user.groups.values_list('id', 'name')),
            'permissions': get_permission_names(user),  # 用自定义函数转换['权限名1', '权限名2', '权限名3',](去重)
            'direct_permissions': list(user.user_permissions.all().values_list('id', 'codename', 'name')),
            # 'group_permissions': list(user.get_group_permissions())  # 默认去重
            'group_permissions': get_raw_group_permissions_optimized(user)  # 自定义不去重
        } for user in page_obj],
        'groups': [{
            'id': group.id,
            'name': group.name,
            'permissions': list(group.permissions.values_list('id', 'codename', 'name'))
        } for group in all_groups],
        'permissions': [{
            'id': permission.id,
            'name': permission.name,
            'codename': permission.codename
        } for permission in all_permissions],

    }

    html = render_to_string('account/rbac/user_group_mgr.html', context, request=request)

    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        # 返回JSON给分页
        return JsonResponse({
            "table_html": html,
            "pagination": pagination,  # 分页
            "context": context,
        })

    # 返回JSON给base主页
    return JsonResponse({
        'html': html,
        'data': {'tabId': 'user_group_mgr'},
        'js_files': [{
            'type': 'esm',  # 声明模块类型
            'path': '/static/js/user_group_mgr.js',
            'inlineCode': None
        }],
    })


# 3-1. 权限配置保存
class SaveUserPermissionsView(APIView):
    def post(self, request):
        # 获取前端数据
        user_id = request.data.get('userID')
        group_ids = request.data.get('groupIDs', [])
        permission_ids = request.data.get('permissionIDs', [])

        # 数据校验
        if not user_id:
            return Response({"error": "没有选择用户"}, status=status.HTTP_400_BAD_REQUEST)
        # if not group_ids and not permission_ids:
        #     return Response({"error": "没有配置组权限或者直接权限"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            user = User.objects.get(id=user_id)

            # 更新组关系
            groups = Group.objects.filter(id__in=group_ids)
            user.groups.set(groups)  # 使用set()原子操作更新关系

            # 更新直接权限
            permissions = Permission.objects.filter(id__in=permission_ids)
            user.user_permissions.set(permissions)  # 原子操作更新权限

            # 清除权限缓存
            if hasattr(user, '_perm_cache'):
                del user._perm_cache  # 防止旧缓存影响后续权限检查

            return Response({
                "success": True,
                "userID": user.id,
                'groups': list(user.groups.values_list('id', 'name')),
                'group_permissions': get_raw_group_permissions_optimized(user),  # 自定义不去重
                'direct_permissions': list(user.user_permissions.all().values_list('id', 'codename', 'name')),
                'permissions': get_permission_names(user),  # 用自定义函数转换['权限名1', '权限名2', '权限名3',]
            })

        except User.DoesNotExist:
            return Response({"error": "用户不存在"}, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


def get_custom_permissions():
    # 获取模型对应的ContentType
    user_info_ct = ContentType.objects.get_for_model(User)  # Jewelry | 用户

    # 从模型Meta中提取自定义权限的codename
    # [('access_account_page', '可访问管理后台'), ('view_external', '可以查看外部客户'), ('system_settings', '系统设置'), ('system_settings.user_manage', '用户管理'),...]
    custom_perms = User._meta.permissions
    # ['access_account_page', 'view_external', 'system_settings', 'system_settings.user_manage',...]
    custom_codenames = [perm[0] for perm in custom_perms]

    # 查询数据库权限
    return Permission.objects.filter(
        content_type=user_info_ct,
        codename__in=custom_codenames
    )


def get_raw_group_permissions_optimized(user):
    if user.is_superuser:
        return list(Permission.objects.values_list('codename', flat=True))

    """优化版带预取的权限获取（仅codename）原先过来之前已经预取了,这里就不用再预取了"""
    # groups = user.groups.prefetch_related(
    #     Prefetch('permissions',
    #              queryset=Permission.objects.only('codename'))  # 只返回codename,不带app名
    # )
    groups = user.groups.all()
    return [
        perm.codename  # 直接取codename字段
        for group in groups
        for perm in group.permissions.all()
    ]


# ################ 后边这些已经集成到上边视图了,以后可以删除掉 #####################
# @require_http_methods(["GET"])
# def rbac_search(request):
#     search_type = request.GET.get('type')
#     query = request.GET.get('q', '')
#
#     results = []
#     if search_type == 'user':
#         results = User.objects.filter(
#             Q(username__icontains=query) |
#             Q(phone__icontains=query)
#         ).values('id', 'username', 'phone')[:10]
#
#     elif search_type == 'group':
#         results = Group.objects.filter(
#             name__icontains=query
#         ).values('id', 'name')[:10]
#
#     elif search_type == 'permission':
#         results = Permission.objects.filter(
#             Q(name__icontains=query) |
#             Q(codename__icontains=query)
#         ).values('id', 'name', 'codename')[:10]
#
#     return JsonResponse(list(results), safe=False)
#
#
# @require_http_methods(["POST"])
# @transaction.atomic
# def update_permissions(request):
#     try:
#         data = json.loads(request.body)
#
#         # 更新用户组关系
#         users = User.objects.filter(id__in=data.get('users', []))
#         for user in users:
#             user.groups.set(data.get('groups', []))
#
#         # 更新组权限关系
#         groups = Group.objects.filter(id__in=data.get('groups', []))
#         for group in groups:
#             group.permissions.set(data.get('permissions', []))
#
#         return JsonResponse({
#             'status': 'success',
#             'message': '权限配置更新成功'
#         })
#
#     except Exception as e:
#         return JsonResponse({
#             'status': 'error',
#             'message': str(e)
#         }, status=400)
#
#
# # views.py
# @require_http_methods(["POST"])
# @transaction.atomic
# def save_permissions(request):
#     try:
#         config_data = json.loads(request.body)['config']
#
#         # 更新用户组关系
#         for user_id, data in config_data.items():
#             user = User.objects.get(id=user_id)
#             user.groups.set(data['groups'])
#             user.user_permissions.set(data['permissions'])
#
#         return JsonResponse({'status': 'success'})
#
#     except Exception as e:
#         return JsonResponse({'status': 'error', 'message': str(e)}, status=400)
#
#
# # views.py
# @transaction.atomic
# def save_user_permissions(request, user_id):
#     user = get_object_or_404(User, id=user_id)
#
#     # 只处理直接权限
#     selected_perms = json.loads(request.POST.get('direct_perms', '[]'))
#
#     # 正确写法（无需转义符）
#     all_direct_perms = Permission.objects.filter(
#         ~Q(group__user=user)  # 波浪线直接放在Q对象前
#     ).values_list('codename', flat=True)
#
#     invalid_perms = set(selected_perms) - set(all_direct_perms)
#     if invalid_perms:
#         return JsonResponse({
#             'status': 'error',
#             'message': f'包含不可修改的权限: {", ".join(invalid_perms)}'
#         }, status=400)
#
#     # 更新直接权限
#     user.user_permissions.set(
#         Permission.objects.filter(codename__in=selected_perms)
#     )
#
#     return JsonResponse({'status': 'success'})
